package pacman;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

/**
 * The Pacman's game manager
 */
public class cCore {
    
    private iCase[][] Plateau;

    private ArrayList<iGhost> GhostList;

    private ArrayList<iFood> FoodList;

    private ArrayList<cPacman> PacmanList=new ArrayList<cPacman>();
    
    private ArrayList<iView> ViewList=new ArrayList<iView>();
    
    private ArrayList<iParser> ParserList=new ArrayList<iParser>();
    
    private int PlateauWidth;
    
    private int PlateauHeight;
    
    private Timer TimTurn;
    private Timer TimBonus;
    
    private boolean isTerminated=false;
    
    private boolean isPaused=false;
    
    private boolean isWinned=false;
    
    private boolean isGameOvered=false;
   

    /**
     * Creates a new instance of cCore.
     */
    public cCore() {
        ParserList.add(new cTxtParser(this));
    }
    
    /**
     * Starts the game
     */
    public void Start(){
        TimTurn=new Timer();
        TimTurn.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                NextTurn();
            }
        },0,200);
        
        TimBonus=new Timer();
        TimBonus.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                AddBonus();
            }
        },0,10000);
    }

    /**
     * Return the game's map
     * @return A 2 dimentionnal array of cCase
     */
    public iCase[][] getPlateau() {
        return Plateau;
    }

    /**
     * Set the game's map
     * @param val A 2 dimentionnal array of cCases
     */
    public void setPlateau(iCase[][] val) {
        this.Plateau = val;
        this.PlateauHeight = Plateau[0].length;
        this.PlateauWidth = Plateau.length;
    }

    /**
     * Return the GhostList
     * @return An ArrayList of iGhost
     */
    public ArrayList<iGhost> getGhostList() {
        return GhostList;
    }

    /**
     * Sets the GhostList
     * @param val An ArrayList of iGhost
     */
    public void setGhostList(ArrayList<iGhost> val) {
        this.GhostList = val;
    }

    /**
     * Return the FoodList
     * @return An ArrayList of iFood
     */
    public ArrayList<iFood> getFoodList() {
        return FoodList;
    }

    /**
     * Sets the FoodList
     * @param val An ArrayList of iFood
     */
    public void setFoodList(ArrayList<iFood> val) {
        this.FoodList = val;
    }

    /**
     * Return the PacmanList
     * @return An ArrayList of cPacman
     */
    public ArrayList<cPacman> getPacmanList() {
        return PacmanList;
    }

    /**
     * Sets the PacmanList
     * @param val An ArrayList of cPacman
     */
    public void setPacmanList(ArrayList<cPacman> val) {
        this.PacmanList = val;
    }

    /**
     * Does the next turn in the game
     */
    public void NextTurn() {
        iCtrl controler;
        cPacman pacman;
        Position newpos;
        
        for(int i=0; i<PacmanList.size(); i++) {
            pacman = PacmanList.get(i);
            pacman.Move();
            
            for(int j=0; j<GhostList.size(); j++) {
                if(GhostList.get(j).getPosition().equals(pacman.getPosition())) {
                    pacman.die();
                }
            }
        }
        
        for(int i=0; i<GhostList.size(); i++) {
            iGhost ghost = GhostList.get(i);
            ghost.Move();
            
            for(int j=0; j<PacmanList.size(); j++) {
                if(PacmanList.get(j).getPosition().equals(ghost.getPosition())){
                    PacmanList.get(j).die();
                }
            }
        }
        
        boolean isAlive=false;
        for(int i=0;i<PacmanList.size();i++){
            if(!PacmanList.get(i).IsDead()){
                isAlive=true;
            }
        }
        if(!isAlive){
            GameOver();
        }
        
        for(int i=0;i<ViewList.size();i++){
            ViewList.get(i).Redraw();
        }
    }
    
    /**
     * Turns the game in Game Over mode
     */
    public void GameOver(){
        isGameOvered=true;
        Terminate();
    }
    
    /**
     * Turns the game in winned mode
     */
    public void Win(){
        isWinned=true;
        Terminate();
    }
    
    /**
     * Terminates the game
     */
    public void Terminate(){
        TimTurn.cancel();
        isTerminated=true;
    }

    /**
     * Returns if the game is terminated
     * @return True if the game is terminated
     */
    public boolean isTerminated(){
        return isTerminated;
    }
    
    /**
     * Returns if the game is winned
     * @return True if the game is winned
     */
    public boolean isWinned(){
        return isWinned;
    }
    
    /**
     * Returns if the game is in Game Over mode
     * @return True if the game is in Game Over mode
     */
    public boolean isGameOvered(){
        return isGameOvered;
    }
    
    /**
     * Load a new map and starts the game
     * @param path Name of the map in the maps directory
     * @throws pacman.ParserNotFoundException If no parser is found to load the map
     */
    public void LoadMap(String path) throws ParserNotFoundException {
        String ext=path.substring(path.length()-3,path.length());
        iParser parser;
        
        for(int i=0;i<ParserList.size();i++){
            parser=ParserList.get(i);
            
            if(parser.getSupportedExt().equalsIgnoreCase(ext)){
                parser.LoadMap(path);
                this.setPlateau(parser.getMap());
                this.FoodList=parser.getFoodList();
                this.GhostList=parser.getGhostList();
                this.PacmanList=parser.getPacmanList();
                this.isTerminated=false;
                this.isWinned=false;
                this.isGameOvered=false;
                this.isPaused=false;
                Start();
                return;
            }
        }
        
        throw(new ParserNotFoundException());
    }
    
    /**
     * Link a controler with an available Pacman
     * @param controler The controler to link with a Pacman
     * @return The Pacman Linked with the controler, null if no there is no available Pacman
     */
    public cPacman setPacmanControler(iCtrl controler){
        cPacman pacman;
        for(int i=0;i<PacmanList.size();i++){
            pacman=PacmanList.get(i);
            if(pacman.getControler()==null){
                pacman.setControler(controler);
                return pacman;
            }
        }
        return null;
    }
    
    /**
     * Returns if a case of the map is accessible
     * @param pos Position of the case
     * @param caller Object who wants to access the case
     * @return True if the case is accessible
     */
    public boolean isAccessible(Position pos, Object caller){
        return Plateau[pos.getX()][pos.getY()].isAccessible(caller);
    }
    
    /**
     * Sets the list of views
     * @param val An ArrayList of iView
     */
    public void setViewList(ArrayList<iView> val){
        ViewList=val;
    }
    
    /**
     * Add a view
     * @param view The iView to add
     */
    public void addView(iView view){
        ViewList.add(view);
    }
    
    /**
     * Sets the list of parsers
     * @param ParserList An ArrayList of iParser
     */
    public void setParserList(ArrayList<iParser> ParserList){
        this.ParserList=ParserList;
    }
    
    /**
     * Returns the list of parsers
     * @return An ArrayList of iParser
     */
    public ArrayList<iParser> getParserList(){
        return ParserList;
    }

    /**
     * Eats the food on a given position
     * @param pos Position of food
     * @param pacman Pacman who eats
     * @return True if a food is eaten
     */
    public boolean Eat(Position pos, cPacman pacman){
        for(int i=0;i<FoodList.size();i++){
            if(FoodList.get(i).getPosition().equals(pos)){
                FoodList.get(i).Eat(pacman);
                FoodList.remove(i);
                return true;
            }
        }
        return false;
    }
    
    /**
     * Does action when a case is accessed
     * @param pos Position of the case witch is accessed
     * @param pacman Pacman who accesses the case
     */
    public void Access(Position pos, cPacman pacman){
        Plateau[pos.getX()][pos.getY()].Access(pacman);
    }
    
    /**
     * Toggle the pause mode
     */
    public void Pause(){
        if(!isPaused && !isTerminated){
            TimTurn.cancel();
            isPaused=true;
        }else if(!isTerminated){
            TimTurn=new Timer();
            TimTurn.scheduleAtFixedRate(new TimerTask() {
                public void run() {
                    NextTurn();
                }
            },0,200);
            isPaused=false;
        }
        
        for(int i=0;i<ViewList.size();i++){
            ViewList.get(i).Redraw();
        }
    }
    
    /**
     * Return if the game is in pause mode
     * @return True if the game is in pause mode
     */
    public boolean isPaused(){
        return isPaused;
    }
    
    /**
     * Add a shrimp in a random position
     */
    public void AddBonus(){
        if(isPaused || isTerminated){
            return;
        }
        Random rnd=new Random();
        Position pos=new Position(rnd.nextInt(PlateauWidth),rnd.nextInt(PlateauHeight));
        
        for(int i=0;i<FoodList.size();i++){
            if(FoodList.get(i).getPosition().equals(pos)){
                return;
            }
        }
        if(isAccessible(pos,this)){
            FoodList.add(new cShrimp(pos));
        }
    }
}
